package cn.b.sky.shop.storage.service.impl;

import cn.b.sky.shop.storage.Bucket;
import cn.b.sky.shop.storage.CloudObjectMetadata;
import cn.b.sky.shop.storage.ProgressListener;
import cn.b.sky.shop.storage.TransferStrategy;
import cn.b.sky.shop.storage.enums.LimiterType;
import cn.b.sky.shop.storage.exception.StorageException;
import cn.b.sky.shop.storage.service.CloudStorageClient;
import cn.b.sky.shop.storage.utils.IOUtil;
import cn.b.sky.shop.storage.utils.RateLimitInputStream;
import com.google.common.util.concurrent.RateLimiter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;

/**
 * @Author: lidapeng
 * @Description:
 * @Date Created in 8/22/23 4:22 PM
 * @Modified By: lidapeng
 * Copyright(c) dbappsecurity.com.cn
 */
@Slf4j
@Data
public abstract class AbstractCloudStorageClient implements CloudStorageClient {

    protected static final byte[] empty_bytes = new byte[0];
    private RateLimiter rateLimiter;
    protected String bucketName;
    protected List<TransferStrategy> transferStrategies;
    protected Map<String, String> defaultHeaders;

    public AbstractCloudStorageClient(String bucketName) {
        this(bucketName, null, (Map)null);
    }


    public AbstractCloudStorageClient(String bucketName, List<TransferStrategy> transferStrategies, Map<String, String> defaultHeaders) {
        this.log = LoggerFactory.getLogger(this.getClass());
        this.defaultHeaders = new HashMap(0);
        this.bucketName = bucketName;
        if (defaultHeaders != null) {
            this.defaultHeaders = defaultHeaders;
        }

        this.setTransferStrategies(transferStrategies);
    }
    protected abstract String preparePath(String path);

    /**
     * 判断是否存在桶
     */
    @Override
    public boolean bucketExists(String object) {
        return this.getObjectMeta(object) != null;
    }

    /**
     * 创建桶
     *
     * @param bucket 桶
     * @param region 区域
     */
    @Override
    public void createForBucket(String bucket, String region) {

    }

    /**
     * 桶列表
     *
     * @return
     */
    @Override
    public List<Bucket> listBuckets() {
        return null;
    }

    /**
     * 存储对象
     *
     * @param objectName       对象名
     * @param object           对象内容，byte，file，stream等
     * @param metadata         文件的基本信息
     * @param progressListener 监听器
     */
    @Override
    public void putObject(String objectName, Object object, CloudObjectMetadata metadata, ProgressListener progressListener) {

        if (objectName.endsWith("/")) {
            throw new StorageException("文件不能以'/'结尾");
        } else {
            String cosPath = this.preparePath(objectName);
            if (object instanceof String) {
                File f = new File((String)object);
                if (f.exists()) {
                    object = f;
                }
            }

            if (object instanceof byte[]) {
                byte[] data = (byte[])((byte[])object);
                if (metadata == null) {
                    metadata = new CloudObjectMetadata(cosPath, new Date(), (long)data.length);
                }

                object = new ByteArrayInputStream(data);
            }

            Closeable closeable = null;
            if (object instanceof File) {
                File file = (File)object;
                if (metadata == null) {
                    metadata = new CloudObjectMetadata(cosPath, new Date(file.lastModified()), file.length());
                } else {
                    metadata.setContentLength(file.length());
                }

                try {
                    object = closeable = new FileInputStream(file);
                } catch (FileNotFoundException var12) {
                    throw new StorageException("文件不存在：" + file.getAbsolutePath());
                }
            }

            try {
                this.checkFileSize(metadata != null ? metadata.getContentLength() : null);
                if (!(object instanceof InputStream)) {
                    throw new StorageException("上传操作不支持的类型 : " + (object != null ? object.getClass() : object));
                }

                this.putObject(objectName, this.prepareInputStream((InputStream)object), metadata, progressListener);
            } finally {
                IOUtil.close(closeable);
            }

        }
    }

    protected void checkFileSize(Long size) {
        if (size != null && this.transferStrategies != null) {
            Iterator var2 = this.transferStrategies.iterator();

            while(var2.hasNext()) {
                TransferStrategy transferStrategy = (TransferStrategy)var2.next();
                if (transferStrategy.getLimiterType() == LimiterType.LimitSize && transferStrategy.getValue() > 0L && size > transferStrategy.getValue()) {
                    throw new StorageException("文件大小超过限制：(" + size + ">" + transferStrategy.getValue() + ")");
                }
            }
        }

    }

    protected InputStream prepareInputStream(InputStream inputStream) {
        if (this.transferStrategies != null) {
            Iterator var2 = this.transferStrategies.iterator();

            while(var2.hasNext()) {
                TransferStrategy transferStrategy = (TransferStrategy)var2.next();
                if (transferStrategy.getLimiterType() == LimiterType.LimitSpeed && transferStrategy.getValue() > 0L && !(inputStream instanceof RateLimitInputStream)) {
                    return new RateLimitInputStream(inputStream, transferStrategy.getValue());
                }
            }
        }

        return inputStream;
    }
    /**
     * 存储对象
     *
     * @param bais
     * @param metadata
     * @param progressListener
     */
    protected abstract void putObject(String ojectName, InputStream bais, CloudObjectMetadata metadata, ProgressListener progressListener);

    public void setTransferStrategies(List<TransferStrategy> transferStrategies) {
        this.transferStrategies = transferStrategies;
        if (this.transferStrategies != null) {
            Iterator var2 = this.transferStrategies.iterator();

            while(var2.hasNext()) {
                TransferStrategy transferStrategy = (TransferStrategy)var2.next();
                if (transferStrategy.getLimiterType() == LimiterType.LimitSpeed && transferStrategy.getValue() > 0L) {
                    this.rateLimiter = RateLimiter.create((double)transferStrategy.getValue());
                }
            }
        }

    }
}
